#include "dominion.h"
#include "dominion_helpers.h"
#include "sput/sput.h"
#include "rngs.h"
#include <stdlib.h>

#define TEST_RUNS 1000
#define TEST_CARD baron

struct gameState *testState;

static void test_card(void)
{
    int i;
    // Baron uses choice 1 to signify whether or not to discard an estate.
    // 1 = 'yes', 0 = 'no' (actually, any value greater than 0 is 'yes')
    int choice1;
    int choice2 = -1;
    int choice3 = -1;
    int *bonus = NULL;

    int init_estate_supply, post_estate_supply;
    int estate_gain;
    int init_hand_estates, post_hand_estates;
    int init_discard_estates, post_discard_estates;
    int init_total_estates, post_total_estates;
    int init_buys, post_buys;
    int init_coins, post_coins;
    int init_hand_count, post_hand_count;
    int init_deck_count, post_deck_count;
    int init_discard_count, post_discard_count;
    int init_total_count, post_total_count;
    int init_game_total_count, post_game_total_count;
    int init_game_counts[MAX_DECK], post_game_counts[MAX_DECK];

    int run;
    int res;
    int player;
    int handPos;
    int *hand;
    int *deck;
    int *discard;

    for(run = 0; run < TEST_RUNS; run++) {
        randState(testState, adventurer, action);
        player = testState->whoseTurn;

        // Randomly choose whether or not to discard an estate
        choice1 = randRange(0, 1);

        hand = testState->hand[player];
        deck = testState->deck[player];
        discard = testState->discard[player];

        init_hand_count = testState->handCount[player];
        init_deck_count = testState->deckCount[player];
        init_discard_count = testState->discardCount[player];
        init_total_count = init_hand_count + init_deck_count + init_discard_count;

        handPos = get_first_in_pile(hand, init_hand_count, TEST_CARD);

        init_game_total_count = get_total_card_counts(testState, init_game_counts);
        init_total_estates = init_game_counts[estate];

        init_buys = testState->numBuys;
        init_coins = testState->coins;

        init_hand_estates = count_cards(hand, init_hand_count, estate);
        init_discard_estates = count_cards(discard, init_discard_count, estate);

        init_estate_supply = testState->supplyCount[estate];
        estate_gain = init_estate_supply ? 1 : 0;

        /* PLAY CARD */
        res = cardEffect(TEST_CARD, choice1, choice2, choice3, testState, handPos, bonus);

        // This probably isn't necessary, given that each pile variable is a
        // pointer, and the value of the pointers should not have changed...
        hand = testState->hand[player];
        deck = testState->deck[player];
        discard = testState->discard[player];

        post_hand_count = testState->handCount[player];
        post_deck_count = testState->deckCount[player];
        post_discard_count = testState->discardCount[player];
        post_total_count = post_hand_count + post_deck_count + post_discard_count;

        post_game_total_count = get_total_card_counts(testState, post_game_counts);
        post_total_estates = post_game_counts[estate];

        post_buys = testState->numBuys;
        post_coins = testState->coins;

        post_hand_estates = count_cards(hand, post_hand_count, estate);
        post_discard_estates = count_cards(discard, post_discard_count, estate);
        post_estate_supply = testState->supplyCount[estate];

        sput_fail_unless(init_game_total_count == post_game_total_count,
                         "total cards in game same after play [initial: %d | post: %d]\n",
                         init_game_total_count, post_game_total_count);
        sput_fail_unless(post_buys == init_buys + 1,
                         "number of buys increased by 1 [initial: %d | post: %d]\n",
                         init_buys, post_buys);

        if(choice1 > 0 && init_hand_estates > 0) {
            sput_fail_unless(post_coins == init_coins + 4,
                             "number of coins increased by 4 [initial: %d | post %d]\n",
                             init_coins, post_coins);
            sput_fail_unless(post_hand_estates == init_hand_estates - 1,
                             "number of estates in hand decreased by 1 [initial: %d | post %d]\n",
                             init_hand_estates, post_hand_estates);
            sput_fail_unless(post_discard_estates == init_discard_estates + 1,
                             "number of estates in discard increased by 1 [initial: %d | post %d]\n",
                             init_discard_estates, post_discard_estates);
        } else {
            sput_fail_unless(init_coins == post_coins,
                             "number of coins remained the same [initial: %d | post %d]\n",
                             init_coins, post_coins);
            sput_fail_unless(init_hand_estates == post_hand_estates,
                             "number of estates in hand remained the same [initial: %d | post %d]\n",
                             init_hand_estates, post_hand_estates);
            sput_fail_unless(post_discard_estates == init_discard_estates,
                             "number of estates in discard pile did not increase\n"
                             "[initial: %d | post: %d]\n",
                             init_discard_estates, post_discard_estates);
            sput_fail_unless(post_estate_supply == init_estate_supply - estate_gain,
                             "number of estates in supply decreased by proper amount\n"
                             "[initial: %d | post: %d | proper change: %d]\n",
                             init_estate_supply, post_estate_supply, estate_gain);
        }
    }
}

int main(int argc, char *argv[])
{
    // Initialize game state
    testState = newGame();

    SelectStream(DEFAULT_STREAM);
    PlantSeeds(-1);

    sput_start_testing();
    sput_enter_suite(__FILE__);

    sput_run_test(test_card);
    sput_finish_testing();

    free(testState);

    return sput_get_return_value();
}
